import Event from 'modules/Event';

const isPhone = window.navigator.userAgent.match(/phone|android|ios/i);
const [touchstart, touchmove, touchend, touchleave] = isPhone
    ? ['touchstart', 'touchmove', 'touchend', 'touchleave']
    : ['mousedown', 'mousemove', 'mouseup', 'mouseleave'];

const { $ } = window;

// 每个dom上都有原始位置
export default class Touch extends Event {
    enableTime = 150;

    canMove = false;

    // 是否要响应touchmove事件
    isExchange = false;

    // 是否进行了位置交换
    isTouchStart = false;

    // 是否触发touchstart事件
    state = {
        start: { x: 0, y: 0 },
        move: { x: 0, y: 0 },
        last: { x: 0, y: 0 },
        index: 0,
    };

    animationing = false;

    // 是否在运动中
    data = {};

    // 元素上绑定的数据
    get realP() {
        const {
            origin, last, start, move
        } = this.state;
        const xMin = origin.x + last.x + move.x - start.x;
        const yMin = origin.y + last.y + move.y - start.y;
        return {
            xMin,
            yMin,
            xMax: xMin + origin.w,
            yMax: yMin + origin.h,
        };
    }

    /**
     * Creates an instance of Touch.
     * @param {HTMLElement} $dom
     * @memberof Touch
     */
    constructor($dom, data = {}) {
        super();
        // 计算所有位置
        this.$dom = $dom;

        this.state.origin = {
            x: $($dom).position().left,
            y: $($dom).position().top,
            w: $dom.clientWidth,
            h: $dom.clientHeight,
        };
        this.getIndex();
        this.addEvents();
        this.data = data;
    }

    getIndex() {
        [...this.$dom.parentElement.children]
            .filter(i => i.classList.contains('sort-ceil'))
            .some((ele, index) => {
                if (ele === this.$dom) {
                    this.state.index = index;
                    return true;
                }
                return false;
            });
    }

    getSiblingsInfo() {
        this.siblingsInfo = [...this.$dom.parentElement.children]
            .filter(i => i.touch && i !== this.$dom)
            .sort((a, b) => a.touch.state.index - b.touch.state.index);
        return this.siblingsInfo;
    }

    getOverlap = () => {
        const { origin } = this.state;
        const { realP: touchElePos } = this;

        // 获取重叠面积
        const getArea = (info1, info2) => {
            const xMin = Math.max(info1.xMin, info2.xMin);
            const yMin = Math.max(info1.yMin, info2.yMin);
            const xMax = Math.min(info1.xMax, info2.xMax);
            const yMax = Math.min(info1.yMax, info2.yMax);
            if (xMin > xMax) return 0;
            if (yMin > yMax) return 0;
            return (xMax - xMin) * (yMax - yMin);
        };

        // 是否有元素还在运动中
        const haveDomAnimation = this.siblingsInfo.some(e => e.touch.animationing);
        !haveDomAnimation
            && this.siblingsInfo.some(ele => {
                const { touch: targetTouch } = ele;
                // 重叠面积需要大于1/3
                if (getArea(touchElePos, targetTouch.realP) > (origin.w * origin.h) / 4) {
                    const { index: currentIndex } = this.state;
                    this.state.index = targetTouch.state.index;
                    if (currentIndex > targetTouch.state.index) {
                        const needMoveTouches = this.siblingsInfo.slice(targetTouch.state.index, currentIndex);
                        const sps = needMoveTouches.map(i => ({ ...i.touch.staticPosition }));
                        sps.push({ ...this.staticPosition });
                        needMoveTouches.forEach((dom, index) => {
                            dom.touch.state.index += 1;
                            dom.touch.staticPosition = sps[index + 1];
                        });
                        this.staticPosition = sps[0]; // eslint-disable-line
                    } else {
                        const needMoveTouches = this.siblingsInfo.slice(currentIndex, targetTouch.state.index);
                        const sps = needMoveTouches.map(i => ({ ...i.touch.staticPosition }));
                        sps.unshift({ ...this.staticPosition });
                        needMoveTouches.forEach((dom, index) => {
                            dom.touch.state.index -= 1;
                            dom.touch.staticPosition = sps[index];
                        });
                        this.staticPosition = sps[sps.length - 1];
                    }
                    this.isExchange = true;
                    return true;
                }
                return false;
            });
    };

    _staticPosition = null;

    get staticPosition() {
        return (
            this._staticPosition || {
                ...this.state.origin,
            }
        );
    }

    set staticPosition(newValue) {
        this._staticPosition = { ...newValue };
        if (!this.canMove) {
            const { origin } = this.state;
            this.state.last = {
                x: newValue.x - origin.x,
                y: newValue.y - origin.y,
            };
            this.updatePosition(0, 0, true);
        }
    }

    addEvents = () => {
        const { $dom } = this;
        $dom.addEventListener(touchstart, this.touchstart);
        $dom.addEventListener(touchmove, this.touchmove);
        $dom.addEventListener(touchend, this.touchend);
        !isPhone && $dom.addEventListener(touchleave, this.touchend);
    };

    timeout = null;

    // 获取触摸点位置信息
    getPosition(e, index = 0) {
        return {
            [`x${index || ''}`]: isPhone ? e.touches[index].clientX : e.clientX,
            [`y${index || ''}`]: isPhone ? e.touches[index].clientY : e.clientY,
        };
    }

    // 更新元素位置
    updatePosition(x, y, animation = false, type = '') {
        const last = { ...this.state.last };
        const { canMove } = this;
        requestAnimationFrame(() => {
            const time = 0.3;
            if (animation) {
                this.animationing = true;
                this.$dom.classList.add('transition');
                this.$dom.style.cssText += `
                    -webkit-transition: -webkit-transform ${time}s, opacity ${time}s;
                    transition: transform ${time}s, opacity ${time}s;
                `;
                this.$dom.clientHeight; // eslint-disable-line
            } else {
                this.$dom.style.cssText += `
                    -webkit-transition: none;
                    transition: none;
                `;
            }

            let extCss = ';';
            if (type === 'start') {
                extCss = 'scale3d(1.06, 1.06, 1); opacity: 0.8;';
            } else if (type === 'end') {
                extCss = '; opacity: 1;';
            }
            this.$dom.style.cssText += `
                position: relative;
                ${canMove && 'z-index: 100;'}
                -webkit-transform: translate3d(${last.x + x}px, ${last.y + y}px, 0) ${extCss}
                transform: translate3d(${last.x + x}px, ${last.y + y}px, 0) ${extCss}
            `;

            // 进行重叠判断
            !animation && this.getOverlap();

            // 计算有效重叠
            if (animation && type !== 'start') {
                clearTimeout(this.updateTimeout);
                this.updateTimeout = setTimeout(() => {
                    this.$dom.style.cssText += `
                        z-index: 0;
                        -webkit-transform: translate3d(${last.x + x}px, ${last.y + y}px, 0);
                        transform: translate3d(${last.x + x}px, ${last.y + y}px, 0);
                    `;
                    if (this.isExchange) {
                        this.isExchange = false;
                        if (!this.$dom?.parentElement?.children) return;
                        const newList = [...this.$dom.parentElement.children]
                            .filter(i => i.touch)
                            .sort((a, b) => a.touch.state.index - b.touch.state.index)
                            .map(i => i.touch.data);
                        this.trigger('change', newList);
                    }
                    this.$dom.classList.remove('transition');
                    this.animationing = false;
                }, time * 1000 + 50);
            }
        });
    }

    getDis() {
        const { move, start } = this.state;
        return {
            x: move.x - start.x,
            y: move.y - start.y,
        };
    }

    updateLast() {
        const { last, origin } = this.state;
        const { staticPosition } = this;
        last.x = staticPosition.x - origin.x;
        last.y = staticPosition.y - origin.y;
        this.updatePosition(0, 0, true, 'end');
    }

    touchstart = event => {
        $('textarea,input').blur();
        if (
            this.$dom.classList.contains('transition') // 当前元素还在执行动画
            || event.target.classList.contains('other-touch-action') // 忽略拖拽手势的元素
            || $(event.target).parents('.other-touch-action').length
            || this.getSiblingsInfo().some(e => e.touch.animationing) // 有其他元素还在执行动画中
        ) {
            return;
        }
        clearTimeout(this.timeout);
        event.preventDefault();
        event.stopPropagation();
        this.isTouchStart = true;
        this.timeout = setTimeout(() => {
            // 获取位置
            this.getSiblingsInfo();
            this.canMove = true;
            this.updatePosition(0, 0, true, 'start');
        }, this.enableTime);
        this.state.start = this.getPosition(event);
    };

    /**
     * @param {Event} event
     * @memberof Touch
     */
    touchmove = event => {
        const { canMove, state } = this;
        if (!canMove) {
            // clearTimeout(this.timeout);
            return;
        }
        event.preventDefault();
        event.stopPropagation();
        state.move = this.getPosition(event);
        const { x, y } = this.getDis();
        this.updatePosition(x, y, false, 'start');
    };

    touchend = event => {
        clearTimeout(this.timeout);
        if (
            event.target.classList.contains('other-touch-action')
            || $(event.target).parents('.other-touch-action').length
        ) {
            return;
        }

        event.preventDefault();
        event.stopPropagation();
        if (!this.canMove) {
            this.isTouchStart && this.trigger('click', event);
        } else {
            this.updateLast();
        }
        this.isTouchStart = false;
        this.canMove = false;
        this.state.move = { x: 0, y: 0 };
        this.state.start = { x: 0, y: 0 };
    };
}
